﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Proggy.Web.Cms.Repositories;
using Proggy.Data.Entities;
using Proggy.Data;
using Proggy.Web.Cms.Service;
using Proggy.Web.Cms.Repositories.ContentModel;
using Proggy.Web.Cms.ContentModel;
using System.Threading;
using Proggy.Web.Cms.Repositories.Membership;
using Proggy.Web.Cms.ViewModels;
using Proggy.Web.Cms.Controllers.FrontEnd;
using Proggy.Web.Cms.Context;
using Proggy.Web.Cms.Cache;
using Proggy.Web.Cms.Membership;
using System.Web;
using Proggy.Web.Cms.Repositories.Published;

namespace Proggy.Web.Cms.Controllers.BackEnd
{
    /// <summary>
    /// ~/Proggy/Content
    /// </summary>
    public class ContentController : BackEndControllerBase
    {
        /// <summary>
        /// Vm repo (public)
        /// </summary>
        public IContentModelTypeRepository ContentModelTypeRepository { get; set; }

        /// <summary>
        /// Content crud service
        /// </summary>
        public IContentCrudService ContentCrudService { get; set; }

        /// <summary>
        /// Memb repo
        /// </summary>
        public IMembershipRepository MembershipRepository { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="service"></param>
        public ContentController(IContentCrudService service, IContentModelTypeRepository cmRepo, IMembershipRepository membRepo, IPublishedContentRepository pubRepo)
        {
            this._pubRepo = pubRepo;
            this.ContentCrudService = service;
            this.ContentModelTypeRepository = cmRepo;
            this.MembershipRepository = membRepo;
        }

        private IPublishedContentRepository _pubRepo;

        /// <summary>
        /// ~/Proggy/Content
        /// </summary>
        /// <returns></returns>
        public ActionResult Index(int? open)
        {
            //return View(this.ContentCrudRepository.GetRootNode());
            return View(this.ContentCrudService.GetContentHomeViewModel());
        }

        /// <summary>
        /// Create a new node
        /// </summary>
        /// <param name="parentNodeId"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Create(int? parentNodeId)
        {
            // start url slug
            var urlSlug = ProggyDataConstants.TEMP_ROOT_URL;

            // creating root content
            if (parentNodeId == null)
            {
                // get allowed root types
                var allowedTypes = this.ContentCrudService.GetAllowedRootTypesSelectList();

                // get allowed view model types
                ViewBag.AllowedViewModelTypes = allowedTypes;
            }
            else
            {
                // has parent, so set url to null
                urlSlug = null;

                // get available type
                var availableType = this.ContentCrudService.GetAvailableContentModelTypeFromNodeId((int)parentNodeId);

                // get allowed view model types
                ViewBag.AllowedViewModelTypes = this.ContentCrudService.GetAllowedChildAvailableTypesSelectList(availableType);
            }

            // return view
            return View("Create", new PublishableNode() { UrlPortion = urlSlug });
        }

        /// <summary>
        /// Add template (POST)
        /// </summary>
        /// <param name="model"></param>
        /// <param name="parentNodeId"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Create(PublishableNode model, int? parentNodeId)
        {
            // check "is back"
            if (ViewBag.IsBack == null || !ViewBag.IsBack)
            {
                // check state
                if (ModelState.IsValid)
                {
                    //return CreateAddTemplate(model);
                    return CreateAddTemplate(model, parentNodeId);
                }
            }

            // creating root content
            if (parentNodeId == null)
            {
                // get allowed root types
                var allowedTypes = this.ContentCrudService.GetAllowedRootTypesSelectList();

                // get allowed view model types
                ViewBag.AllowedViewModelTypes = allowedTypes;
            }
            else
            {
                // get available type
                var availableType = this.ContentCrudService.GetAvailableContentModelTypeFromNodeId((int)parentNodeId);

                // get allowed view model types
                ViewBag.AllowedViewModelTypes = this.ContentCrudService.GetAllowedChildAvailableTypesSelectList(availableType);
            }

            //CAN'T HAVE TEMPLATE YET BECAUSE TYPE NOT SELECTED...
            //ViewBag.AllowedTemplates = this.ContentCrudRepository.GetAllowedTemplatesSelectList(availableType);

            return View("Create", model);
        }

        /// <summary>
        /// Add template (POST)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult CreateAddTemplate(PublishableNode model, int? parentNodeId)
        {
            // get available type
            var availableType = this.ContentCrudService.GetAvailableTypeFromTypeAlias(model.TypeAlias);

            // set allowed templates
            ViewBag.AllowedTemplates = this.ContentCrudService.GetAllowedTemplatesSelectList(availableType);

            // 
            return View("CreateAddTemplate", model);
        }

        /// <summary>
        /// Try to finish creation of node (POST)
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult TryFinishCreate(PublishableNode model, int? parentNodeId)
        {
            // set model metadata
            var now = DateTime.Now;
            model.FirstCreated = now;
            model.LastUpdated = now;
            model.LastUpdatedUsername = MembershipContext.Current.CurrentMember.Username;
            model.FirstCreatedUsername = MembershipContext.Current.CurrentMember.Username;

            if (ModelState.IsValid)
            {
                // get action
                var which = Request.Form["WhichButton"];
                var isBack = which == "Back";
                var isNext = which == "Create";

                // back
                if (isBack)
                {
                    ViewBag.IsBack = true;
                    return Create(model, parentNodeId);
                }

                // next
                if (isNext)
                {

                    // get error
                    var errorString = string.Empty;
                    var newPublishableNodeId = 0;


                    //
                    if (this.ContentCrudService.CreateNewNode(model, parentNodeId, ref errorString, ref newPublishableNodeId))
                    {
                        return RedirectToAction("Edit", new { id = newPublishableNodeId, success = 3 });
                    }
                    else
                    {
                        ModelState.AddModelError("", errorString);
                    }
                }
            }

            // all else fails
            return CreateAddTemplate(model, parentNodeId);
        }

        /// <summary>
        /// Publish node(GET)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet, ProggyBackEndAuthorize(MembershipConstants.PUBLISHER_GROUP_NAME)]
        public ActionResult Publish(int id)
        {
            // get content model
            var cm = this.ContentCrudService.ContentModelDataRepository.GetDraftContentModelFromId(id);

            // check
            if (cm == null)
            {
                this.Response.StatusCode = 404;
                return null;
            }

            // 
            return View(cm);
        }

        /// <summary>
        /// Publish node (POST)
        /// </summary>
        /// <param name="model"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost, ProggyBackEndAuthorize(MembershipConstants.PUBLISHER_GROUP_NAME)]
        public ActionResult Publish(IContentModelBase model, int id)
        {
            //
            if (ModelState.IsValid)
            {
                // variables
                var errorMsg = string.Empty;

                //
                if (this.ContentCrudService.PublishDraftContent(model, MembershipContext.Current.CurrentMember.Username, ref errorMsg))
                {
                    // get app helper
                    return RedirectToAction("Index", new { success = 2, selected = id });
                }

                //
                ModelState.AddModelError("", errorMsg);
            }

            //
            return View(model);
        }

        /// <summary>
        /// Edit draft
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Edit(int id)
        {
            // get draft
            var model = this.ContentCrudService.ContentModelDataRepository.GetDraftContentModelFromId(id);

            // get available type
            var availableType = this.ContentCrudService.GetAvailableTypeFromTypeAlias(model.TypeAlias);

            // set allowed templates
            ViewBag.AllowedTemplates = this.ContentCrudService.GetAllowedTemplatesSelectList(availableType);

            //
            return View(model);
        }

        /// <summary>
        /// Save draft
        /// </summary>
        /// <param name="model"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Edit(IContentModelBase model, int id)
        {
            // start error
            var errorMsg = "";

            //
            if (ModelState.IsValid)
            {
                // save                
                if (this.ContentCrudService.SaveDraftContent(model, MembershipContext.Current.CurrentMember.Username, ref errorMsg))
                {
                    // redirect
                    return RedirectToAction("Edit", new { id = model.Id, success = 1 });
                }
            }

            // get available type
            var availableType = this.ContentCrudService.GetAvailableTypeFromTypeAlias(model.TypeAlias);

            // set allowed templates
            ViewBag.AllowedTemplates = this.ContentCrudService.GetAllowedTemplatesSelectList(availableType);

            // add error
            ModelState.AddModelError("", errorMsg);
            ModelState.AddModelError(ProggyDataConstants.CHANGES_NOT_SAVED, ProggyDataConstants.CHANGES_NOT_SAVED);

            // issues
            return View(model);
        }

        /// <summary>
        /// Revert to published (GET)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Revert(int id)
        {
            // get content
            var model = this.ContentCrudService.GetPublishableNodeFromId(id);

            //
            return View(model);
        }

        /// <summary>
        /// Revert to published (POST)
        /// </summary>
        /// <param name="model"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Revert(PublishableNode model, int id)
        {
            // start error
            var errorMsg = "";

            // remove model errors
            var errors = ModelState.ToList();
            foreach (var error in errors)
            {
                ModelState.Remove(error);
            }

            // revert
            if (this.ContentCrudService.RevertToPublished(model.PublishableNodeId, ref errorMsg))
            {
                return RedirectToAction("Index", new { success = 7, selected = model.PublishableNodeId });
            }

            //
            ModelState.AddModelError("", errorMsg);
            return View(model);
        }

        /// <summary>
        /// Trash a node / nodes
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Trash(int id)
        {
            // get content
            var model = this.ContentCrudService.GetPublishableNodeFromId(id);

            //
            return View(model);
        }

        /// <summary>
        /// Trash a node (nodes)
        /// </summary>
        /// <param name="model"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Trash(PublishableNode model, int id)
        {
            // start error
            var errorMsg = "";

            // remove model errors
            var errors = ModelState.ToList();
            foreach (var error in errors)
            {
                ModelState.Remove(error);
            }

            // revert
            if (this.ContentCrudService.Trash(model.PublishableNodeId, MembershipContext.Current.CurrentMember.Username, ref errorMsg))
            {
                return RedirectToAction("Index", new { success = 6 });
            }

            //
            ModelState.AddModelError("", errorMsg);
            return View(model);
        }

        /// <summary>
        /// Shows trashed content
        /// </summary>
        /// <returns></returns>
        [ChildActionOnly]
        public PartialViewResult TrashedContentPartial()
        {
            // get content
            var content = this.ContentCrudService.GetTrashedContent().OrderByDescending(x => x.LastUpdated).ToList();

            // check
            if (content.Count < 1)
            {
                return null;
            }

            // 
            return PartialView("_TrashedContentPartial", content);
        }

        /// <summary>
        /// Delete trashed item permanently
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet, ProggyBackEndAuthorize(MembershipConstants.PUBLISHER_GROUP_NAME)]
        public ActionResult Delete(int id)
        {

            //
            return View();
        }

        /// <summary>
        /// Delete trashed item permanently
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost, ProggyBackEndAuthorize(MembershipConstants.PUBLISHER_GROUP_NAME)]
        public ActionResult Delete(object model, int id)
        {
            // start error
            var errorMsg = "";

            // 
            if (ModelState.IsValid)
            {

                //
                if (this.ContentCrudService.DeleteTrashedContent(id, ref errorMsg))
                {
                    return RedirectToAction("Index", new { success = 10 });
                }
            }

            //
            ModelState.AddModelError("", errorMsg);
            return View();

        }

        /// <summary>
        /// Sitemap
        /// </summary>
        /// <param name="open"></param>
        /// <param name="selected"></param>
        /// <returns></returns>
        [HttpGet]
        public PartialViewResult SiteMap(string areaId, string open, string selected, bool includeContentMenu, bool asPublished)
        {

            // get view model
            var viewModel = this.ContentCrudService.GetSiteMapViewModel(areaId, open, selected, includeContentMenu, asPublished);

            // return
            return PartialView(viewModel);

        }

        /// <summary>
        /// Preview content
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Preview(int id)
        {
            // get model
            var model = this.ContentCrudService.ContentModelDataRepository.GetDraftContentModelFromId(id);

            // create context
            var context = new FrontEndRequestContext
            {
                CurrentContentModel = model,
                CurrentCachedContentRepository = this._pubRepo,
                RootUrl = FrontEndRequestContext.GetRootUrl()
            };

            // force
            FrontEndRequestContext.ForceCurrent(context);

            // get type data
            var typeData = model.GetTypeData();

            // get controller
            var controller = (FrontEndControllerBase)Activator.CreateInstance(typeData.GetControllerType());

            // 
            return controller.HandleUrl(model.Id);
        }

        /// <summary>
        /// Sort children
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME + "," + MembershipConstants.PUBLISHER_GROUP_NAME)]
        public ActionResult SortChildren(int id)
        {
            // get model
            var model = this.ContentCrudService.GetSortChildContentViewModel(id);

            //
            return View(model);
        }

        /// <summary>
        /// POST sort children
        /// </summary>
        /// <param name="model"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult SortChildren(SortChildContentViewModel model, int id)
        {
            // get new model
            var newModel = this.ContentCrudService.GetSortedSortChildContentViewModel(model, id);

            // start error
            var error = "";

            //
            if (ModelState.IsValid)
            {
                if (this.ContentCrudService.SaveNewChildContentModelOrder(newModel, ref error))
                {
                    return RedirectToAction("Index", new { selected = id, open = id, success = 15 });
                }

                // add error
                ModelState.AddModelError("", error);
            }

            //
            return View(newModel);
        }

        /// <summary>
        /// Select a parent node to move a node to
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult MoveSelector(int id)
        {
            // get model
            var model = this.ContentCrudService.ContentModelDataRepository.GetDraftContentModelFromId(id);

            // check
            if (model == null)
            {
                throw new HttpException(404, "Page not found");
            }
            return View(model);
        }

        /// <summary>
        /// Move - GET
        /// </summary>
        /// <param name="id"></param>
        /// <param name="nodeToMoveId"></param>
        /// <returns></returns>
        [HttpGet]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Move(int id, int nodeToMoveId)
        {
            //
            return View();
        }

        /// <summary>
        /// Move - POST
        /// </summary>
        /// <param name="id"></param>
        /// <param name="nodeToMoveId"></param>
        /// <returns></returns>
        [HttpPost]
        [ProggyBackEndAuthorize(MembershipConstants.EDITOR_GROUP_NAME)]
        public ActionResult Move(bool dummyBool, int id, int nodeToMoveId)
        {
            //
            var movingToNodeId = id;
            var errorMsg = string.Empty;

            // check
            if (this.ContentCrudService.Move(nodeToMoveId, movingToNodeId, ref errorMsg))
            {
                return Content("<a id=\"proggy_contenthasmoved\" href=\"" + Url.Action("Index", new { selected = nodeToMoveId, success = 9 }) + "\">Click here to continue</a>");
            }

            // error
            ModelState.AddModelError("", "CANNOT COMPLETE MOVE - " + errorMsg);
            return View();
        }

        /// <summary>
        /// Restore trashed content - GET
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        public ActionResult Restore(int id)
        {
            // get model
            var model = this.ContentCrudService.GetTrashedNodeById(id);

            //
            return View(model);
        }

        /// <summary>
        /// Restore trashed content - GET
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Restore(TrashedNode model, int id)
        {
            // start error
            var error = "";
            var newId = 0;


            // check
            if (ModelState.IsValid)
            {
                // restore
                if (this.ContentCrudService.RestoreTrashedContent(model, MembershipContext.Current.CurrentMember.Username, ref error, ref newId))
                {
                    return RedirectToAction("Index", new { success = 16, selected=newId });
                }
            }

            // add to errors
            ModelState.AddModelError("", error);

            //
            return View(model);
        }        
    }
}
